package org.langcom.logic.schedule;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.Instant;
import org.joda.time.MutableDateTime;

public abstract class TimeItem implements Serializable {

	private static final long serialVersionUID = 7874849734253902116L;
	private Integer number;
	private List<TimeItem> childList = new ArrayList<TimeItem>();
	private TimeItem parent;
	private MutableDateTime now;

	public TimeItem() {
	}

	public TimeItem(Integer number) {
		this.number = number;
	}

	public Integer getNumber() {
		return number;
	}

	public void setNumber(Integer number) {
		this.number = number;
	}

	public Long getNextTime() {
		provideParents();
		MutableDateTime instance = new Instant().toMutableDateTime();
		// instance.setYear(1970);
		// instance.setMonthOfYear(1);
		// instance.setDayOfMonth(1);
		// instance.setHourOfDay(0);
		// instance.setMinuteOfHour(0);
		// instance.setSecondOfMinute(0);
		instance.setMillisOfSecond(0);
		// instance.setMillis(0);
		MutableDateTime now = new Instant().toMutableDateTime();
		try {
			getNextTime(instance, now);
		} catch (NeverException e) {
			return null;
		}
		// System.out.println(now + "-------------adfsg------------" + instance);
		return instance.getMillis();
	}

	public void getNextTime(MutableDateTime dateTime, MutableDateTime now) throws NeverException {
		this.now = now;
		dateTime.setMillisOfSecond(0);
		if (number == null) {
			setChildTime(dateTime);
		} else {
			int nowValue = getNowValue(now);
			if (nowValue > number) {
				addFirstLevel(dateTime);
				setTime(dateTime);
			} else if (nowValue == number) {
				setChildTime(dateTime);
			} else {
				setTime(dateTime);
			}
		}
		if (dateTime.isEqual(now) || dateTime.isBefore(now)) {
			addFirstLevel(dateTime);
		}
	}

	private void addFirstLevel(MutableDateTime dateTime) throws NeverException {
		TimeItem parent2 = getParent();
		TimeItem current = this;
		while (parent2 != null) {
			current = parent2;
			parent2 = parent2.getParent();
		}
		current.addNextLevel(dateTime);
	}

	private void setTime(MutableDateTime dateTime) {
		if (number != null) {
			switch (getType()) {
				case YEAR:
					dateTime.setYear(number);
					break;
				case MONTH:
					dateTime.setMonthOfYear(number);
					break;
				case WEEK:
					dateTime.setWeekOfWeekyear(number);
					break;
				case DAY:
					dateTime.setDayOfMonth(number);
					break;
				case DAY_OF_WEEK:
					dateTime.setDayOfWeek(number);
					break;
				case HOUR:
					dateTime.setHourOfDay(number);
					break;
				case MINUTE:
					dateTime.setMinuteOfHour(number);
					break;
				case SECOND:
					dateTime.setSecondOfMinute(number);
					break;
				case MiILLISECOND:
					dateTime.setMillisOfSecond(number);
					break;
			}
		}
		setChildTime(dateTime);
	}

	private void setChildTime(MutableDateTime dateTime) {
		if (childList.size() == 1) {
			childList.get(0).setTime(dateTime);
		} else if (childList.size() > 1) {
			MutableDateTime min = dateTime;
			boolean childWorks = false;
			for (TimeItem item : childList) {
				MutableDateTime copy = dateTime.copy();
				item.setTime(copy);
				// System.out.println(min + "--------------dd---asdfg------" + copy + "------------" + now);
				if (copy.isAfter(min)) {
					min = copy;
					childWorks = true;
				}
			}
			if (childWorks) {
				dateTime.setDateTime(
					min.getYear(),
					min.getMonthOfYear(),
					min.getDayOfMonth(),
					min.getHourOfDay(),
					min.getMinuteOfHour(),
					min.getSecondOfMinute(),
					0);
			}
		}
	}

	private void addNextLevel(MutableDateTime dateTime) throws NeverException {
		switch (getType()) {
			case YEAR:
				throw new NeverException();
			case MONTH:
				dateTime.addYears(1);
				break;
			case WEEK:
				dateTime.addMonths(1);
				break;
			case DAY:
				dateTime.addMonths(1);
				break;
			case DAY_OF_WEEK:
				dateTime.addWeeks(1);
				break;
			case HOUR:
				dateTime.addDays(1);
				break;
			case MINUTE:
				dateTime.addHours(1);
				break;
			case SECOND:
				dateTime.addMinutes(1);
				break;
			case MiILLISECOND:
				dateTime.addSeconds(1);
				break;
		}
	}

	public int getNowValue(MutableDateTime dateTime) {
		switch (getType()) {
			case YEAR:
				return dateTime.getYear();
			case MONTH:
				return dateTime.getMonthOfYear();
			case WEEK:
				return dateTime.getWeekyear();
			case DAY:
				return dateTime.getDayOfMonth();
			case DAY_OF_WEEK:
				return dateTime.getDayOfWeek();
			case HOUR:
				return dateTime.getHourOfDay();
			case MINUTE:
				return dateTime.getMinuteOfHour();
			case SECOND:
				return dateTime.getSecondOfMinute();
			case MiILLISECOND:
				return dateTime.getMillisOfSecond();
		}
		return number;
	}

	public ItemType getType() {
		return ItemType.NONE;
	}

	public TimeItem addChild(TimeItem child) {
		childList.add(child);
		child.setParent(this);
		return child;
	}

	private void setParent(TimeItem parent) {
		this.parent = parent;
	}

	public TimeItem addChildMore(TimeItem child) {
		childList.add(child);
		child.setParent(this);
		return this;
	}

	public TimeItem getParent() {
		return parent;
	}

	public List<TimeItem> getChildList() {
		return childList;
	}

	public void setChildList(List<TimeItem> childList) {
		this.childList = childList;
	}

	@Override
	public String toString() {
		return getType() + " - " + (number == null ? "" : number) + " children: " + getChildList();
	}

	public void removeChld(TimeItem timeItem) {
		childList.remove(timeItem);
	}

	public void provideParents() {
		for (TimeItem item : childList) {
			item.setParent(this);
			item.provideParents();
		}
	}
}
